<?php
if (!extension_loaded('gd')) return;

/**
 * From an original idea of :
 *
 * @author  Jose Rodriguez <jose.rodriguez@exec.cl>
 * @license GPLv3
 * @link    http://code.google.com/p/cool-php-captcha
 * @package captcha
 * @version 0.3
 *
 */

class MP_Form_field_type_captcha_gd2_googlelike
{
	public $width  = 200;														/** Width of the image */
	public $height = 70;														/** Height of the image */
	public $minWordLength = 5;													/** Min word length (for non-dictionary random text generation) */
	/**
	 * Max word length (for non-dictionary random text generation)
	 * 
	 * Used for dictionary words indicating the word-length
	 * for font-size modification purposes
	 */
	public $maxWordLength = 8;
	public $session_var = 'mp_googlelike';											/** Sessionname to store the original text */
	public $backgroundColor = array(255, 255, 255);										/** Background color in RGB-array */
	public $colors = array(array(27,78,181)/*blue*/, array(22,163,35)/*green*/, array(214,36,7) /*red*/); /** Foreground colors in RGB-array */
	public $shadowColor = null; //array(0, 0, 0);										/** Shadow color in RGB-array or null */

	public $fonts = array(
		'Antykwa'  => array('spacing' => -3, 'minSize' => 27, 'maxSize' => 30, 'font' => 'AntykwaBold.ttf'),				//
		'Candice'  => array('spacing' =>-1.5,'minSize' => 28, 'maxSize' => 31, 'font' => 'Candice.ttf'),				// Font configuration
		'DingDong' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 30, 'font' => 'Ding-DongDaddyO.ttf'),		 	// - font: TTF file
		'Duality'  => array('spacing' => -2, 'minSize' => 30, 'maxSize' => 38, 'font' => 'Duality.ttf'),				// - spacing: relative pixel space between character
		'Heineken' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 34, 'font' => 'Heineken.ttf'),				// - minSize: min font size
		'Jura'     => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 32, 'font' => 'Jura.ttf'),					// - maxSize: max font size
		'StayPuft' => array('spacing' =>-1.5,'minSize' => 28, 'maxSize' => 32, 'font' => 'StayPuft.ttf'),
		'VeraSans' => array('spacing' => -1, 'minSize' => 20, 'maxSize' => 28, 'font' => 'VeraSansBold.ttf'),
		'VeraSeBd'	=> array('spacing' => -2, 'minSize' => 24, 'maxSize' => 30, 'font' => 'VeraSeBd.ttf'),
	);

	public $Yperiod	= 12;														/** Wave configuracion in X and Y axes */
	public $Yamplitude = 14;
	public $Xperiod	= 11;
	public $Xamplitude = 5;

	public $maxRotation = 8;													/** letter rotation clockwise */

	/**
	 * Internal image size factor (for better image quality)
	 * 1: low, 2: medium, 3: high
	 */
	public $scale = 2;

	/** 
	 * Blur effect for better image quality (but slower image processing).
	 * Better image results with scale=3
	 */
	public $blur = false;

	/** Debug? */
	public $debug = false;
	
	/** Image format: jpeg or png */
	public $imageFormat = 'png';


	/** GD image */
	public $im;

	function __construct()
	{
		session_start();

		$root = dirname(__FILE__) . '/words/';
		if (is_dir($root)) 
		{
			$dir = @opendir($root);
			if ($dir) while (($file = readdir($dir)) !== false)  if (($file{0} != '.') && (substr($file, -4) == '.php'))  $xlangs[] = strtolower(substr($file, 0, -4));
			@closedir($dir);
		}

		$lang = $xlangs[array_rand($xlangs)];
		if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) 
		{
			$lang_http  = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
			if (in_array($lang_http, $xlangs)) $lang = $lang_http;
		}
		$this->wordsFile = $root . "$lang.php";

		$this->CreateImage();

		ob_end_clean();
		if ($this->imageFormat == 'png' && function_exists('imagepng')) 
		{
		    header('Content-type: image/png');
		    imagepng($this->im);
		} else {
		    header('Content-type: image/jpeg');
		    imagejpeg($this->im, null, 80);
		}
		imagedestroy ($this->im);
		die();
	}

	public function CreateImage() {
		$ini = microtime(true);

		/** Initialization */
		$this->ImageAllocate();
        
		/** Text insertion */
		$text = $this->GetCaptchaText();

		$fontcfg  = $this->fonts[array_rand($this->fonts)];
		$this->WriteText($text, $fontcfg);

		$_SESSION[$this->session_var] = $text;

		/** Transformations */
		$this->WaveImage();
		if ($this->blur && function_exists('imagefilter')) imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
		$this->ReduceImage();

		if ($this->debug) imagestring($this->im, 1, 1, $this->height-8, "$text {$fontcfg['font']} ".round((microtime(true)-$ini)*1000)."ms", $this->GdFgColor );
	}

	/**
	* Creates the image resources
	*/
	protected function ImageAllocate() 
	{
		// Cleanup
		if (!empty($this->im)) imagedestroy($this->im);

		$this->im = imagecreatetruecolor($this->width*$this->scale, $this->height*$this->scale);

		// Background color
		$this->GdBgColor = imagecolorallocate($this->im, $this->backgroundColor[0], $this->backgroundColor[1], $this->backgroundColor[2] );
		imagefilledrectangle($this->im, 0, 0, $this->width*$this->scale, $this->height*$this->scale, $this->GdBgColor);

		// Foreground color
		$color = $this->colors[mt_rand(0, sizeof($this->colors)-1)];
		$this->GdFgColor = imagecolorallocate($this->im, $color[0], $color[1], $color[2]);

		// Shadow color
		if (!empty($this->shadowColor) && is_array($this->shadowColor) && sizeof($this->shadowColor) >= 3) $this->GdShadowColor = imagecolorallocate($this->im, $this->shadowColor[0], $this->shadowColor[1], $this->shadowColor[2] );
	}

	/**
	* Text generation
	*
	* @return string Text
	*/
	protected function GetCaptchaText() 
	{
		$text = $this->GetDictionaryCaptchaText();
		if (!$text) $text = $this->GetRandomCaptchaText();
		return $text;
	}

	/**
	* Random dictionary word generation
	*
	* @param boolean $extended Add extended "fake" words
	* @return string Word
	*/
	function GetDictionaryCaptchaText($extended = false) 
	{
		if (empty($this->wordsFile)) return false;

		$fp     = fopen($this->wordsFile, "r");
		$length = strlen(fgets($fp));
		if (!$length) return false;

		$line   = rand(1, (filesize($this->wordsFile)/$length)-2);
		if (fseek($fp, $length*$line) == -1) return false;

		$text = trim(fgets($fp));
		fclose($fp);

		/** Change ramdom volcals */
		if ($extended) 
		{
			$text   = preg_split('//', $text, -1, PREG_SPLIT_NO_EMPTY);
			$vocals = array('a', 'e', 'i', 'o', 'u');
			foreach ($text as $i => $char) if (mt_rand(0, 1) && in_array($char, $vocals)) $text[$i] = $vocals[mt_rand(0, 4)];
			$text = implode('', $text);
		}
		return $text;
	}

	/**
	* Random text generation
	*
	* @return string Text
	*/
	protected function GetRandomCaptchaText($length = null) 
	{
		if (empty($length)) $length = rand($this->minWordLength, $this->maxWordLength);
		$words  = "abcdefghijlmnopqrstvwyz";
		$vocals = "aeiou";
		$text  = "";
		$vocal = rand(0, 1);
		for ($i = 0; $i < $length; $i++) 
		{
			$text .= ($vocal) ? substr($vocals, mt_rand(0, 4), 1) : substr($words, mt_rand(0, 22), 1);
			$vocal = !$vocal;
		}
		return $text;
	}

	/**
	* Text insertion
	*/
	protected function WriteText($text, $fontcfg = array()) 
	{
	// Select the font configuration
		if (empty($fontcfg)) $fontcfg  = $this->fonts[array_rand($this->fonts)];

		// Full path of font file
		$fontfile = dirname(__FILE__) . '/fonts/' . $fontcfg['font'];
		/**
         *  Increase font-size for shortest words: 9% for each glyp missing */
		$lettersMissing = $this->maxWordLength-strlen($text);
		$fontSizefactor = 1+($lettersMissing*0.09);

		// Text generation (char by char)
		$x      = 20*$this->scale;
		$y      = round(($this->height*27/40)*$this->scale);
		$length = strlen($text);
		for ($i = 0; $i < $length; $i++) 
		{
			$degree   = rand($this->maxRotation*-1, $this->maxRotation);
			$fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize'])*$this->scale*$fontSizefactor;
			$letter   = substr($text, $i, 1);

			if ($this->shadowColor) $coords = imagettftext($this->im, $fontsize, $degree, $x+$this->scale, $y+$this->scale, $this->GdShadowColor, $fontfile, $letter);
			$coords = imagettftext($this->im, $fontsize, $degree, $x, $y, $this->GdFgColor, $fontfile, $letter);
			$x += ($coords[2]-$x) + ($fontcfg['spacing']*$this->scale);
		}
	}

	/**
	* Wave filter
	*/
	protected function WaveImage() 
	{
		// X-axis wave generation
		$xp = $this->scale*$this->Xperiod*rand(1,3);
		$k = rand(0, 100);
		for ($i = 0; $i < ($this->width*$this->scale); $i++)  imagecopy($this->im, $this->im, $i-1, sin($k+$i/$xp) * ($this->scale*$this->Xamplitude), $i, 0, 1, $this->height*$this->scale);

		// Y-axis wave generation
		$k = rand(0, 100);
		$yp = $this->scale*$this->Yperiod*rand(1,2);
		for ($i = 0; $i < ($this->height*$this->scale); $i++) imagecopy($this->im, $this->im, sin($k+$i/$yp) * ($this->scale*$this->Yamplitude), $i-1, 0, $i, $this->width*$this->scale, 1);
	}

	/**
	* Reduce the image to the final size
	*/
	protected function ReduceImage() 
	{
		$imResampled = imagecreatetruecolor($this->width, $this->height);
		imagecopyresampled($imResampled, $this->im, 0, 0, 0, 0, $this->width, $this->height, $this->width*$this->scale, $this->height*$this->scale );
		imagedestroy($this->im);
		$this->im = $imResampled;
	}
}
new MP_Form_field_type_captcha_gd2_googlelike();